package com.easyjob.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.easyjob.entity.constants.Constants;
import com.easyjob.entity.dto.AppUserLoginDto;
import com.easyjob.entity.dto.ExamConfigDto;
import com.easyjob.entity.dto.ImportErrorItem;
import com.easyjob.entity.po.*;
import com.easyjob.entity.vo.QuestionInfoVo;
import com.easyjob.entity.dto.SessionUserAdminDto;
import com.easyjob.entity.enums.PageSize;
import com.easyjob.entity.enums.PostStatusEnum;
import com.easyjob.entity.enums.ResponseCodeEnum;
import com.easyjob.entity.enums.VerifyRegexEnum;
import com.easyjob.entity.query.QuestionInfoQuery;
import com.easyjob.entity.query.SimplePage;
import com.easyjob.entity.vo.PaginationResultVO;
import com.easyjob.entity.vo.app.WrongQuestionVo;
import com.easyjob.exception.BusinessException;
import com.easyjob.mappers.ACommonMapper;
import com.easyjob.mappers.CategoryMapper;
import com.easyjob.mappers.QuestionInfoMapper;
import com.easyjob.mappers.plus.*;
import com.easyjob.service.QuestionInfoService;
import com.easyjob.utils.BeanCopyUtils;
import com.easyjob.utils.ExcelUtils;
import com.easyjob.utils.StringTools;
import com.easyjob.utils.VerifyUtils;
import org.springframework.data.redis.connection.ReactiveListCommands;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * 问题 业务接口实现
 */
@Service("questionInfoService")
public class QuestionInfoServiceImpl implements QuestionInfoService {

    @Resource
    private QuestionInfoMapper<QuestionInfo, QuestionInfoQuery> questionInfoMapper;

    @Resource
    private ACommonMapper aCommonMapper;

    @Resource
    private ChapterPlusMapper chapterPlusMapper;

    @Resource
    private QuestionPlusMapper questionPlusMapper;

    @Resource
    private QuestionItemPlusMapper questionItemPlusMapper;

    @Resource
    private AppExamCountMapper appExamCountMapper;

    @Resource
    private CategoryPlusMapper categoryPlusMapper;

    private final int[] TYPES = new int[]{1, 2, 0, 3};

    /**
     * 根据条件查询列表
     */
    @Override
    public List<QuestionInfo> findListByParam(QuestionInfoQuery param) {
        return this.questionInfoMapper.selectList(param);
    }

    /**
     * 根据条件查询列表
     */
    @Override
    public Integer findCountByParam(QuestionInfoQuery param) {
        return this.questionInfoMapper.selectCount(param);
    }

    /**
     * 分页查询方法
     */
    @Override
    public PaginationResultVO<QuestionInfo> findListByPage(QuestionInfoQuery param) {
        int count = this.findCountByParam(param);
        int pageSize = param.getPageSize() == null ? PageSize.SIZE15.getSize() : param.getPageSize();

        SimplePage page = new SimplePage(param.getPageNo(), count, pageSize);
        param.setSimplePage(page);
        List<QuestionInfo> list = this.findListByParam(param);
        PaginationResultVO<QuestionInfo> result = new PaginationResultVO(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), list);
        return result;
    }

    /**
     * 新增
     */
    @Override
    public Integer add(QuestionInfo bean) {
        return this.questionInfoMapper.insert(bean);
    }

    /**
     * 批量新增
     */
    @Override
    public Integer addBatch(List<QuestionInfo> listBean) {
        if (listBean == null || listBean.isEmpty()) {
            return 0;
        }
        return this.questionInfoMapper.insertBatch(listBean);
    }

    /**
     * 批量新增或者修改
     */
    @Override
    public Integer addOrUpdateBatch(List<QuestionInfo> listBean) {
        if (listBean == null || listBean.isEmpty()) {
            return 0;
        }
        return this.questionInfoMapper.insertOrUpdateBatch(listBean);
    }

    /**
     * 多条件更新
     */
    @Override
    public Integer updateByParam(QuestionInfo bean, QuestionInfoQuery param) {
        StringTools.checkParam(param);
        return this.questionInfoMapper.updateByParam(bean, param);
    }

    /**
     * 多条件删除
     */
    @Override
    public Integer deleteByParam(QuestionInfoQuery param) {
        StringTools.checkParam(param);
        return this.questionInfoMapper.deleteByParam(param);
    }

    /**
     * 根据QuestionId获取对象
     */
    @Override
    public QuestionInfo getQuestionInfoByQuestionId(Integer questionId) {
        return this.questionInfoMapper.selectByQuestionId(questionId);
    }

    /**
     * 根据QuestionId修改
     */
    @Override
    public Integer updateQuestionInfoByQuestionId(QuestionInfo bean, Integer questionId) {
        return this.questionInfoMapper.updateByQuestionId(bean, questionId);
    }

    /**
     * 根据QuestionId删除
     */
    @Override
    public Integer deleteQuestionInfoByQuestionId(Integer questionId) {
        return this.questionInfoMapper.deleteByQuestionId(questionId);
    }

    @Override
    public void saveQuestion(QuestionInfo questionInfo, Boolean superAdmin) {
        Category category = categoryPlusMapper.selectOne(new QueryWrapper<Category>().eq("category_id", questionInfo.getCategoryId()));
        questionInfo.setChapterName(category.getCategoryName());
        if (null == questionInfo.getQuestionId()) {
            questionInfo.setCreateTime(new Date());
            this.questionInfoMapper.insert(questionInfo);
        } else {
            QuestionInfo dbInfo = this.questionInfoMapper.selectByQuestionId(questionInfo.getQuestionId());
            /*if (PostStatusEnum.POST.getStatus().equals(dbInfo.getStatus())) {
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }*/
            //只能自己发布的才能修改
            if (!dbInfo.getCreateUserId().equals(questionInfo.getCreateUserId()) && !superAdmin) {
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }
            questionInfo.setCreateUserId(null);
            questionInfo.setCreateUserName(null);
            questionInfo.setCreateTime(null);
            this.questionInfoMapper.updateByQuestionId(questionInfo, questionInfo.getQuestionId());
        }
    }

    @Override
    public void delQuestionBatch(String questionIds, Integer userId) {
        String[] questionIdArray = questionIds.split(",");
        if (userId != null) {
            QuestionInfoQuery infoQuery = new QuestionInfoQuery();
            infoQuery.setQuestionIds(questionIdArray);
            List<QuestionInfo> questionInfoList = this.questionInfoMapper.selectList(infoQuery);
            List<QuestionInfo> currentUserDataList = questionInfoList.stream()
                    .filter(a -> !a.getCreateUserId().equals(String.valueOf(userId))).collect(Collectors.toList());
            if (!currentUserDataList.isEmpty()) {
                throw new BusinessException(ResponseCodeEnum.CODE_600);
            }
        }
        questionInfoMapper.deleteBatchByQuestionId(questionIdArray, PostStatusEnum.NO_POST.getStatus(), userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<ImportErrorItem> importQuestion(MultipartFile file, SessionUserAdminDto userAdminDto) {
        //查询出所有的分类
        List<Chapter> chapterList = chapterPlusMapper.selectList(null);
        Map<String, Chapter> chapterMap = chapterList.stream().collect(Collectors.toMap(Chapter::getChapterName, Function.identity(), (data1, data2) -> data2));

        List<List<String>> dataList = ExcelUtils.readExcel(file, Constants.EXCEL_TITLE_QUESTION, Constants.ONE);

        //错误列表
        List<ImportErrorItem> errorList = new ArrayList<>();

        //问题列表
        List<QuestionInfo> questionList = new ArrayList<>();

        //数据行编号
        Integer dataRowNum = 2;
        for (List<String> row : dataList) {
            if (errorList.size() > Constants.LENGTH_50) {
                throw new BusinessException("错误数据超过" + Constants.LENGTH_50 + "行，请认真检查数据后再导入");
            }
            dataRowNum++;
            List<String> errorItemList = new ArrayList<>();
            Integer index = 0;
            //标题
            String title = row.get(index++);
            if (StringTools.isEmpty(title) || title.length() > Constants.LENGTH_300) {
                errorItemList.add("标题不能为空，且长度不能超过300");
            }
            //章节
            String chapterName = row.get(index++);
            Chapter chapter = chapterMap.get(chapterName);
            if (chapter == null) {
                errorItemList.add("该章节不存在");
            }
            //难度
            String difficultyLevel = row.get(index++);
            Integer difficultyLevelInt = null;
            if (!VerifyUtils.verify(VerifyRegexEnum.NUMBER_LETTER_UNDER_LINE, difficultyLevel)) {
                difficultyLevelInt = Integer.parseInt(difficultyLevel);
                if (difficultyLevelInt > 5) {
                    errorItemList.add("难度只能是1到5的数字");
                }
            } else {
                errorItemList.add("难度必须是数字");
            }

            //问题描述
            String question = row.get(index++);
            String answerAnalysis = row.get(index++);
            if (StringTools.isEmpty(answerAnalysis)) {
                errorItemList.add("答案解析不能为空");
            }

            //有错误就继续
            if (errorItemList.size() > 0 || errorList.size() > 0) {
                ImportErrorItem errorItem = new ImportErrorItem();
                errorItem.setRowNum(dataRowNum);
                errorItem.setErrorItemList(errorItemList);
                errorList.add(errorItem);
                continue;
            }
            //封装对象
            QuestionInfo questionInfo = new QuestionInfo();
            questionInfo.setTitle(title);
            questionInfo.setChapterId(chapter.getChapterId());
            questionInfo.setChapterName(chapter.getChapterName());
            questionInfo.setDifficultyLevel(difficultyLevelInt);
            questionInfo.setQuestion(question);
            questionInfo.setAnswerAnalysis(answerAnalysis);
            questionInfo.setCreateTime(new Date());
            questionInfo.setStatus(PostStatusEnum.NO_POST.getStatus());
            questionInfo.setCreateUserId(String.valueOf(userAdminDto.getUserId()));
            questionInfo.setCreateUserName(userAdminDto.getUserName());
            questionList.add(questionInfo);
        }
        if (questionList.isEmpty()) {
            return errorList;
        }
        //插入数据库
        this.questionInfoMapper.insertBatch(questionList);
        return errorList;
    }

    @Override
    public QuestionInfo showDetailNext(QuestionInfoQuery query, Integer type, Integer currentId, Boolean updateReadCount) {
        if (type == null) {
            query.setQuestionId(currentId);
        } else {
            query.setNextType(type);
            query.setCurrentId(currentId);
        }
        QuestionInfo questionInfo = questionInfoMapper.showDetailNext(query);
        if (questionInfo == null && type == null) {
            throw new BusinessException("内容不存在");
        } else if (questionInfo == null && type == -1) {
            throw new BusinessException("已经是第一条了");
        } else if (questionInfo == null && type == 1) {
            throw new BusinessException("已经是最后一条了");
        }

        if (updateReadCount && questionInfo != null) {
            //更新阅读数
            aCommonMapper.updateCount(Constants.TABLE_NAME_QUESTION_INFO, 1, null, currentId);
            questionInfo.setReadCount(questionInfo.getReadCount() + 1);
        }
        return questionInfo;
    }

    @Override
    public List<QuestionInfoVo> getInfoByChapterId(Integer id, Integer sortMode) {
        Map<Integer, List<QuestionInfoVo>> map = questionPlusMapper.selectList(new QueryWrapper<ExamQuestion>().
                eq("chapter_id", id).
                eq("status",1)).
                parallelStream().
                map(item -> {
                    QuestionInfoVo questionInfoVo = BeanCopyUtils.copyBean(item, QuestionInfoVo.class);
                    List<ExamQuestionItem> questionItemList = questionItemPlusMapper.selectList(new QueryWrapper<ExamQuestionItem>().eq("question_id", item.getQuestionId()));
                    questionInfoVo.setQuestionItemList(questionItemList);
                    return questionInfoVo;
                }).collect(Collectors.groupingBy(QuestionInfoVo::getQuestionType));
        return convertQuestion2Vo(map, sortMode, null);
    }

    public List<QuestionInfoVo> convertQuestion2Vo( Map<Integer, List<QuestionInfoVo>> map,
                                                    Integer sortMode,
                                                    ExamConfigDto configDto) {
        List<QuestionInfoVo> questionList = new ArrayList<>();
        for (int i = 0; i < TYPES.length; i ++  ) {
            List<QuestionInfoVo> questionInfoVoList = map.get(TYPES[i]);
            if (map.containsKey(TYPES[i])) {
                if (configDto != null) {
                    if(TYPES[i] == 1) {
                        if (configDto.getCount1() != 0) {
                            Collections.shuffle(questionInfoVoList);
                            List<QuestionInfoVo> questionInfoVos = questionInfoVoList.stream().limit(configDto.getCount1()).collect(Collectors.toList());
                            questionList.addAll(questionInfoVos);
                        }
                    } else if (TYPES[i] == 0) {
                        if (configDto.getCount3() != 0) {
                            Collections.shuffle(questionInfoVoList);
                            List<QuestionInfoVo> questionInfoVos = questionInfoVoList.stream().limit(configDto.getCount3()).collect(Collectors.toList());
                            questionList.addAll(questionInfoVos);
                        }
                    } else if (TYPES[i] == 2) {
                        if (configDto.getCount2() != 0) {
                            Collections.shuffle(questionInfoVoList);
                            List<QuestionInfoVo> questionInfoVos = questionInfoVoList.stream().limit(configDto.getCount2()).collect(Collectors.toList());
                            questionList.addAll(questionInfoVos);
                        }
                    } else {
                        if (configDto.getCount4() != 0) {
                            Collections.shuffle(questionInfoVoList);
                            List<QuestionInfoVo> questionInfoVos = questionInfoVoList.stream().limit(configDto.getCount4()).collect(Collectors.toList());
                            questionList.addAll(questionInfoVos);
                        }
                    }
                } else{
                    if (sortMode == 0) questionInfoVoList = questionInfoVoList.stream().sorted(Comparator.comparingInt(QuestionInfoVo::getSort)).collect(Collectors.toList());
                    else if (sortMode == 1) questionInfoVoList = questionInfoVoList.stream().sorted(Comparator.comparingInt(e -> new Random().nextInt())).collect(Collectors.toList());
                    questionList.addAll(questionInfoVoList);
                }
            }
        }
        return questionList;
    }

    @Override
    public List<QuestionInfoVo> getQuestionInfoByConfig(ExamConfigDto configDto, AppUserLoginDto appDto) {
        List<Integer> categoryIds = configDto.getMulti1();
        List<Integer> types = configDto.getRadio2();
        Integer sortMode = configDto.getRadio1();
        LambdaQueryWrapper<Chapter> wrapper = new LambdaQueryWrapper<>();
        if (!categoryIds.isEmpty()) wrapper.in(Chapter::getCategoryId, categoryIds);
        List<Integer> ids = chapterPlusMapper.selectList(wrapper).
                stream().
                map(Chapter::getChapterId).
                collect(Collectors.toList());
        QueryWrapper<ExamQuestion> query = new QueryWrapper<ExamQuestion>();
        if (!ids.isEmpty()) query.in("chapter_id", ids);
        if (!types.isEmpty()) query.in("question_type", types);
        Map<Integer, List<QuestionInfoVo>> map = questionPlusMapper.selectList(query).parallelStream().
                map(item -> {
                    QuestionInfoVo questionInfoVo = BeanCopyUtils.copyBean(item, QuestionInfoVo.class);
                    List<ExamQuestionItem> questionItemList = questionItemPlusMapper.selectList(new LambdaQueryWrapper<ExamQuestionItem>().eq(ExamQuestionItem::getQuestionId, item.getQuestionId()));
                    questionInfoVo.setQuestionItemList(questionItemList);
                    return questionInfoVo;
                }).
                collect(Collectors.groupingBy(QuestionInfoVo::getQuestionType));
        return convertQuestion2Vo(map, sortMode, configDto);
    }

    @Override
    public List<QuestionInfoVo> getInfoByCategoryId(Integer id) {
        List<Integer> ids = chapterPlusMapper.selectList(new LambdaQueryWrapper<Chapter>().eq(Chapter::getCategoryId, id)).
                stream().
                map(Chapter::getChapterId).
                collect(Collectors.toList());
        Map<Integer, List<QuestionInfoVo>> map = questionPlusMapper.selectList(new LambdaQueryWrapper<ExamQuestion>().
                in(ExamQuestion::getChapterId, ids).eq(ExamQuestion::getStatus,1)).stream().
                map(item -> {
                    QuestionInfoVo questionInfoVo = BeanCopyUtils.copyBean(item, QuestionInfoVo.class);
                    List<ExamQuestionItem> questionItemList = questionItemPlusMapper.selectList(new LambdaQueryWrapper<ExamQuestionItem>().eq(ExamQuestionItem::getQuestionId, item.getQuestionId()));
                    questionInfoVo.setQuestionItemList(questionItemList);
                    return questionInfoVo;
                }).
                collect(Collectors.groupingBy(QuestionInfoVo::getQuestionType));
        return convertQuestion2Vo(map, 1, null);
    }

    @Override
    public List<WrongQuestionVo> getWrongQuestionList(AppUserLoginDto appDto) {
        List<WrongQuestionVo> wrongQuestionVoList = new ArrayList<>();
        List<AppExamCount> appExamCountList = appExamCountMapper.selectList(new LambdaQueryWrapper<AppExamCount>().eq(AppExamCount::getUId, appDto.getUserId()));
        if (appExamCountList.size() == 0) return wrongQuestionVoList;
        List<Integer> ids = appExamCountList.stream().
                map(AppExamCount::getQId).
                collect(Collectors.toList());
        Map<Integer, Integer> wrongQuestionMap = appExamCountList.stream().collect(Collectors.toMap(AppExamCount::getQId, AppExamCount::getCount, (o1, o2) -> o2));
       return questionPlusMapper.selectList(new LambdaQueryWrapper<ExamQuestion>().
                in(ExamQuestion::getQuestionId, ids)).stream().
                map(item -> {
                    WrongQuestionVo wrongQuestionVo = BeanCopyUtils.copyBean(item, WrongQuestionVo.class);
                    List<ExamQuestionItem> questionItemList = questionItemPlusMapper.selectList(new LambdaQueryWrapper<ExamQuestionItem>().eq(ExamQuestionItem::getQuestionId, item.getQuestionId()));
                    wrongQuestionVo.setQuestionItemList(questionItemList);
                    wrongQuestionVo.setCount(wrongQuestionMap.get(item.getQuestionId()));
                    return wrongQuestionVo;
                }).
                sorted(Comparator.
                        comparingInt(WrongQuestionVo::getCount).reversed().
                        thenComparing(WrongQuestionVo::getCreateTime))
                .collect(Collectors.toList());
    }
}